﻿using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

using Microsoft.Drawing;

using Spencen.Mobile.UI.Commanding;
using Spencen.Mobile.UI.Primitives;
using Spencen.Mobile.UI.Transforms;

namespace Spencen.Mobile.UI.Controls
{
    public class ButtonBar : DrawingPanel, ICommandSource
    {
        private static readonly Color SelectionGradientTop = Color.FromArgb( 212, 225, 145 );
        private static readonly Color SelectionGradientMiddle = Color.FromArgb( 40, 111, 3 );
        private static readonly Color SelectionGradientBottom = Color.FromArgb( 119, 201, 25 );
        private static readonly Brush SelectionBrush = new SolidBrush( SelectionGradientMiddle );

        private bool _isPressed;

        public readonly static Font DefaultFont = new Font( FontFamily.GenericSansSerif, 11, FontStyle.Regular );
        public readonly static Font DefaultSecondaryFont = new Font( FontFamily.GenericSerif, 8, FontStyle.Regular );
        public readonly static Brush DefaultForeground = new SolidBrush( Color.Black );
        public readonly static Brush DefaultSecondaryForeground = new SolidBrush( Color.FromArgb( 40, 40, 40 ) );
        public readonly static Brush DefaultPressedForeground = new SolidBrush( Color.White );
        public readonly static Brush DefaultSecondaryPressedForeground = new SolidBrush( Color.LightGray );

        public ButtonBar( IDrawingHost host ) : base( host )
        {
            ComposeElements();
            Font = DefaultFont;
            SecondaryFont = DefaultSecondaryFont;
            Foreground = DefaultForeground;
            SecondaryForeground = DefaultSecondaryForeground;
            PressedForeground = DefaultPressedForeground;
            SecondaryPressedForeground = DefaultSecondaryPressedForeground;
            //IsRetained = true;
            CanFocus = true;
        }

        private void ComposeElements()
        {
        }

        protected override void LayoutChildren()
        {
            base.LayoutChildren();
            ComposeElements();
        }

        public string Text { get; set; }
        public string SecondaryText { get; set; }
        public Font Font { get; set; }
        public Brush Foreground { get; set; }
        public Font SecondaryFont { get; set; }
        public Brush SecondaryForeground { get; set; }
        public Brush PressedForeground { get; set; }
        public Brush SecondaryPressedForeground { get; set; }
        public Image Image { get; set; }
        public ICommand Command { get; set; }
        public bool IsPressed { get { return _isPressed; } set { _isPressed = value; Invalidate(); } }

        protected override void OnRender( GraphicsContext context )
        {
            var points = new Point[ 4 ] { new Point( 0, 0 ), new Point( Size.Width, 0 ), new Point( Size.Width, Size.Height ), new Point( 0, Size.Height ) };
            var transformedPoints = context.TransformedPoints( points );
            var frameMargin = new Size( (int)( 8 * context.Graphics.DpiX / 96f ), (int)( 16 * context.Graphics.DpiY / 96f ) );

            if ( IsPressed )
            {
                var fullRectangle = new Rectangle( transformedPoints[0].X, transformedPoints[0].Y, transformedPoints[2].X - transformedPoints[0].X, transformedPoints[2].Y - transformedPoints[0].Y );
                context.Graphics.FillRectangle( SelectionBrush, fullRectangle );
                var topRectangle = new Rectangle(
                    transformedPoints[ 0 ].X,
                    transformedPoints[ 0 ].Y,
                    transformedPoints[ 2 ].X - transformedPoints[ 0 ].X,
                    (int)( ( transformedPoints[ 2 ].Y - transformedPoints[ 0 ].Y ) / 8.0f * 3.0f ) );
                var bottomRectangle = new Rectangle(
                    transformedPoints[ 0 ].X,
                    transformedPoints[ 0 ].Y + (int)( ( transformedPoints[ 2 ].Y - transformedPoints[ 0 ].Y ) / 8.0f * 5.0f ),
                    transformedPoints[ 2 ].X - transformedPoints[ 0 ].X,
                    (int)( ( transformedPoints[ 2 ].Y - transformedPoints[ 0 ].Y ) / 8.0f * 3.0f ) );
                context.Graphics.FillGradientRectangle( topRectangle, SelectionGradientTop, SelectionGradientMiddle, FillDirection.TopToBottom );
                context.Graphics.FillGradientRectangle( bottomRectangle, SelectionGradientMiddle, SelectionGradientBottom, FillDirection.TopToBottom );
                using ( var selectionPen = new Pen( SelectionGradientMiddle ) )
                    context.Graphics.DrawRectangle( selectionPen, fullRectangle );
            }
            else
            {
                if ( Background == null || Background is SolidBrush && ( Background as SolidBrush ).Color == Color.Transparent )
                { }   // Don't paint
                else
                    context.Graphics.FillPolygon( Background, transformedPoints );
                GlassButton.DrawGlass( context, transformedPoints, 150, GlassTheme.LightGlass );
            }

            // Draw children
            base.OnRender( context );

            var imageFrameSize = new Size(
                (int)( Properties.Resources.ButtonFrame.Width * context.Graphics.DpiX / 192f ),
                (int)( Properties.Resources.ButtonFrame.Height * context.Graphics.DpiY / 192f ) );

            // We can't rotate the image (frame) yet but at least position it around its transformed centre point.
            var imagePoints = new Point[] { new Point( frameMargin.Width + imageFrameSize.Width / 2, Size.Height / 2 ) };
            var transformedImagePoints = context.TransformedPoints( imagePoints );
            context.Graphics.DrawImage( 
                Properties.Resources.ButtonFrame,
                new Rectangle(
                    transformedImagePoints[ 0 ].X - imageFrameSize.Width / 2,
                    transformedImagePoints[ 0 ].Y - imageFrameSize.Height / 2,
                    imageFrameSize.Width,
                    imageFrameSize.Height ),
                new Rectangle( 0, 0, Properties.Resources.ButtonFrame.Width, Properties.Resources.ButtonFrame.Height ), 
                GraphicsUnit.Pixel );

            // Draw the image within the frame (using Alpha)
            if ( Image != null )
            {
                // TODO: Take into account original image aspect ratio ( e.g. max dimension)
                var imageSize = new Size( 
                    (int) ( Properties.Resources.ButtonFrame.Width * 0.75 * context.Graphics.DpiX / 192f ),
                    (int)( Properties.Resources.ButtonFrame.Height * 0.75 * context.Graphics.DpiX / 192f ) );

                context.Graphics.DrawImageTransparent( Image,
                    new Rectangle(
                        transformedImagePoints[ 0 ].X - imageSize.Width / 2,
                        transformedImagePoints[ 0 ].Y - imageSize.Height / 2,
                        imageSize.Width,
                        imageSize.Height ) );
            }

            using ( var borderPen = new Pen( Color.Gray ) )
            {
                borderPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                context.Graphics.DrawLine( borderPen, transformedPoints[ 0 ].X, transformedPoints[ 0 ].Y, transformedPoints[ 1 ].X, transformedPoints[ 1 ].Y );
                context.Graphics.DrawLine( borderPen, transformedPoints[ 3 ].X, transformedPoints[ 3 ].Y, transformedPoints[ 2 ].X, transformedPoints[ 2 ].Y );
            }

            if ( HasFocus )
                context.Graphics.DrawPolygon( context.FocusPen, transformedPoints );

            var rotationTransform = Transforms.OfType<RotateTransform>().FirstOrDefault( t => t.Angle != 0.0f );
            if ( rotationTransform != null || Angle != 0.0f )
            {
                using ( Font rotatedFont = DrawingSupport.CreateRotatedFont( context.Graphics, Font.Name, Font.Size, (int)( rotationTransform == null ? Angle : 360 - rotationTransform.Angle ) ),
                    rotatedSecondaryFont = DrawingSupport.CreateRotatedFont( context.Graphics, SecondaryFont.Name, SecondaryFont.Size, (int) ( rotationTransform == null ? Angle : 360 - rotationTransform.Angle ) ) )
                {
                    var textPoints = new Point[] 
                    { 
                        new Point( points[0].X + frameMargin.Width * 2 + imageFrameSize.Width, points[0].Y + (int) (Size.Height * 0.125f) ),
                        new Point( points[0].X + frameMargin.Width * 2 + imageFrameSize.Width, points[0].Y + (int) (Size.Height * 0.55f) )
                    };
                    var transformedTextPoints = context.TransformedPoints( textPoints );
                    context.Graphics.DrawString( Text, rotatedFont, IsPressed ? PressedForeground : Foreground, transformedTextPoints[ 0 ].X, transformedTextPoints[ 0 ].Y, new StringFormat( StringFormatFlags.NoClip ) );
                    context.Graphics.DrawString( SecondaryText, rotatedSecondaryFont, IsPressed ? SecondaryPressedForeground : SecondaryForeground, transformedTextPoints[ 1 ].X, transformedTextPoints[ 1 ].Y, new StringFormat( StringFormatFlags.NoClip ) );
                }
            }
            else
            {
                // TODO: Which is better LogFont or just Drawing.Font?
                //context.Graphics.DrawString( Text, Font, Foreground, transformedPoints[ 0 ].X + frameMargin.Width * 2 + imageFrameSize.Width, transformedPoints[ 0 ].Y + Size.Height * 0.125f );
                //context.Graphics.DrawString( SecondaryText, SecondaryFont, SecondaryForeground, transformedPoints[ 0 ].X + frameMargin.Width * 2 + imageFrameSize.Width, transformedPoints[ 1 ].Y + Size.Height * 0.55f );

                using ( Font textFont = DrawingSupport.CreateRotatedFont( context.Graphics, DefaultFont.Name, DefaultFont.Size, 0 ),
                            secondaryTextFont = DrawingSupport.CreateRotatedFont( context.Graphics, SecondaryFont.Name, SecondaryFont.Size, 0 ) )
                {
                    context.Graphics.DrawString( Text, textFont, IsPressed ? PressedForeground : Foreground, transformedPoints[ 0 ].X + frameMargin.Width * 2 + imageFrameSize.Width, transformedPoints[ 0 ].Y + Size.Height * 0.125f );
                    context.Graphics.DrawString( SecondaryText, secondaryTextFont, IsPressed ? SecondaryPressedForeground : SecondaryForeground, transformedPoints[ 0 ].X + frameMargin.Width * 2 + imageFrameSize.Width, transformedPoints[ 1 ].Y + Size.Height * 0.55f );
                }
            }
        }

        // This is required whilst the contents is rendered entirely in OnRender and not via child elements.
        public override bool HitTest( Point hitPoint, GraphicsContext context )
        {
            return DrawingSupport.PolygonContains( GeneratePoints(), hitPoint );
        }

        protected internal override void OnMouseDown( MouseEventArgs e )
        {
            base.OnMouseDown( e );
            IsPressed = true;
        }

        protected internal override void OnMouseUp( MouseEventArgs e )
        {
            base.OnMouseUp( e );
            
            // TODO: Check that mouse is still within the bounds of the control
            OnClick();
            IsPressed = false;
        }

        public event EventHandler Click;

        protected virtual void OnClick()
        {
            if ( Command != null )
                Command.Execute();

            var handler = Click;
            if ( handler != null )
                handler( this, EventArgs.Empty );
        }

        // HACK: Temporary only till get templating working
        public object Tag { get; set; }
    }
}
